/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include "SceOsMisc.h"
#include "common/FileIO.h"
#include "file_io_MAC_LLC.h"

extern "C" {
  FILEIO_MAC_LLC_EXPORT
  POA_CF::Resource *
  CreateFileInput_MAC_LLC (const char * identifier)
  {
    FileInput_MAC_LLC * f_in;

    if (FileInput_MAC_LLC::Create (f_in, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) f_in;
  }
}

static const unsigned int max_filePackets = 1024;
static const unsigned int max_errorPackets = 1024;

FileInput_MAC_LLC::FileInput_MAC_LLC (const char * identifier)
  : CF__Resource_impl (identifier),
    m_threadrunning (false),
    m_bStop (false)
{
  unsigned int output_buffer_size;

  output_buffer_size = 72;
  m_output = new char [output_buffer_size];
  m_header = new unsigned char [72];

  // Define port.
  m_fileout = new FileOutPort_impl (m_output, output_buffer_size);
  m_mac_err_in = new MAC_ErrCtrl_XmitInPort_impl<FileInput_MAC_LLC> (this);
  m_mac_flow_ctrl_in = new MAC_FlowCtrl_InPort_impl<FileInput_MAC_LLC> (this);
  m_llc_ctrl_out = new MAC_Ctrl_OutPort_impl<SDR_FM3TR::LLC_Control> ();
}

FileInput_MAC_LLC::~FileInput_MAC_LLC ()
{
  delete m_output;
  delete m_header;
  delete m_fileout;
  delete m_mac_err_in;
  delete m_mac_flow_ctrl_in;
  delete m_llc_ctrl_out;
}

status
FileInput_MAC_LLC::Create (FileInput_MAC_LLC *& handle,
                           const char * identifier)
{
  FileInput_MAC_LLC * Temp = new FileInput_MAC_LLC (identifier);

  if (!Temp) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

status
FileInput_MAC_LLC::Init ()
{
#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Initializing." << std::endl;
#endif

  m_bPauseFlow = false;

  // Error packet variables.
  m_errorPackets = new short [max_errorPackets];
  m_numErrorPackets = 0;

  // Initialze variables.
  m_packetCounter = 0;
  m_header_sent = false;

  /*
   * Property UUIDs (from .prf.xml).
   *
   * <simple id="DCE:6bd274cb-4810-42a8-89ed-0f573935e0a9" type="string" name="Input_File_Name" mode="readwrite">
   *   <value>data_files/data_file_input_ASCII.txt</value>
   *   <kind kindtype="configure" />
   * </simple>
   * <simple id="DCE:b0b829f3-649d-4717-bcde-80a599539a32" type="string" name="Input_Data_Type" mode="readwrite">
   *   <value>octet</value>
   *   <kind kindtype="configure" />
   * </simple>
   * <simple id="DCE:5b715020-4f18-42d3-95c3-9af574bd6f58" type="ushort" name="Num_Data_Elements" mode="readwrite">
   *   <value>72</value>
   *   <kind kindtype="configure" />
   * </simple>
   */

  int index;

  try {
    {
      // Get value of Input_File_Name property.
      const char * prop_name = "DCE:6bd274cb-4810-42a8-89ed-0f573935e0a9"; // UUID
      index = GetPropertyIndex (prop_name);

      if (index < 0) {
	return fail;
      }

      if (GetPropertyValue (index, &m_prop_input_file_name)) {
	return fail;
      }
    }

    {
      // Get value of Input_Data_Type property
      const char * prop_name = "DCE:b0b829f3-649d-4717-bcde-80a599539a32"; // UUID
      index = GetPropertyIndex (prop_name);

      if (index < 0) {
	return fail;
      }

      if (GetPropertyValue (index, &m_prop_input_data_type)) {
	return fail;
      }
    }

    {
      // Get value of Num_Data_Elements property
      const char * prop_name = "DCE:5b715020-4f18-42d3-95c3-9af574bd6f58";
      index = GetPropertyIndex (prop_name);

      if (index < 0) {
	return fail;
      }

      if (GetPropertyValue (index, &m_num_data_elems)) {
	return fail;
      }
    }
  }
  catch ( ... ) {
    return fail;
  }

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Initializing; input file name \""
	    << m_prop_input_file_name
	    << "\"." << std::endl;
#endif

  if (!(m_file_in = fopen (m_prop_input_file_name, "rb"))) {
#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Failed to open input file \""
	    << m_prop_input_file_name
	    << "\"." << std::endl;
#endif
  return fail;
  }

  enum IODataType data_type = ConvertStringToDataTypeEnum (m_prop_input_data_type);
  m_data_size = GetSizeOfDataType (data_type);
  AssembleHeader ();
  return success;
}

status
FileInput_MAC_LLC::AssembleHeader()
{
  /*
   * Read the input file and determine how many packets it contains.
   */

  int num_samples;
  m_num_packets=0;
  m_num_bytes_last_packet = 0;
  bool m_eof=false;

  do {
    m_num_packets++;

    // try to read 72 samples
    num_samples = fread (m_output, m_data_size, m_num_data_elems, m_file_in);

    if (num_samples == m_num_data_elems) {
      if (feof (m_file_in)) {
	m_eof = true;
      }
    }
    else {
      if (feof (m_file_in)) {
	m_eof = true;
	if (num_samples == 0) {
	  // Perfect multiple of m_num_data_elems in file.
	  m_num_packets--;
	  m_num_bytes_last_packet = m_num_data_elems;
	}
	else {
	  // Last packet is only partial.
	  m_num_bytes_last_packet = (short) num_samples;
	}
      }
      else {
	return fail;
      }
    }
  }
  while (!m_eof && m_num_packets<max_filePackets);

  if (!m_eof) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Error: File too large!" << std::endl;
#endif
    return fail;
  }

  // reset file pointer to beginning of file
  rewind (m_file_in);

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Input file contains " << m_num_packets << " packets." << std::endl;
#endif

  // Assemble header information
  // BYTES:   1 calling platform address
  //          1 called platform address
  //         11 message ID
  //          2 message length:
  //             1: number of blocks
  //             1: number of bytes in last block
  //         21 padding to fill an entire packet
  // ---------------------------------------------
  // TOTAL:  36 bytes @ 8 bits of info
  //       = 72 bytes @ 4 bits of info

  unsigned char * header_ptr = m_header;

  // Calling platform address
  *header_ptr++ = 1;

  // Called platform address
  *header_ptr++ = 0xaa;

  // Message ID
  memset (header_ptr, 0xFF, 11);
  header_ptr += 11;

  memcpy (header_ptr, &m_num_packets, 2);
  memcpy (header_ptr+2, &m_num_bytes_last_packet, 2);
  header_ptr += 4;

  memset (header_ptr, 0x01, 19);
  header_ptr += 19;

  /*
   * Header: pack 36 bytes @ 8 bits into 72 bytes @ 4 bits.
   * The RsBlockEncoder expects 7-bit information, and it
   * is easy to pack data into 4-bit blocks.
   */

  for (int i=0; i<36; i++) {
    m_output[2*i]     = ((m_header[i] & 0xf0) >> 4);
    m_output[2*i + 1] = m_header[i] & 0x0f;
  }

  return success;
}

status
FileInput_MAC_LLC::AssemblePacket (unsigned int packetID, int & num_samples)
{
  if (packetID > m_num_packets) {
    // PacketID exceeds total number of packets in file.
    return fail;
  }

  // Parse the file for packetID-1 blocks.

  int result = fseek (m_file_in, (packetID-1)*m_num_data_elems, SEEK_SET);

  if (result) {
    // fseek failed!
    return fail;
  }

  if (packetID == m_num_packets) {
    num_samples = m_num_bytes_last_packet;
  }
  else {
    num_samples = m_num_data_elems;
  }

  num_samples = fread (m_output, m_data_size, num_samples, m_file_in);
  return success;
}

status
FileInput_MAC_LLC::AssembleDummyPacket ()
{
  memset (m_output, 0x01, m_num_data_elems);
  return success;
}

void
FileInput_MAC_LLC::signalNAK (SDR_FM3TR::MAC_Signaling::PacketID packetID)
{
  SDR_FM3TR::MAC_Signaling::PacketID my_packetID = packetID;

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: NAK signaled for packet "
	    << packetID << "." << std::endl;
#endif

  if ((my_packetID == (unsigned long) (m_num_packets+1)) && 
      ((m_num_packets%2)==0)) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Ignoring dummy packet (initial)." << std::endl;
#endif
    return;
  }

  // See if the NAK is for a retransmitted packet or not.
  assert (my_packetID>=0);

  unsigned short id = static_cast<short> (my_packetID);
  if (id <= (m_num_packets+1)) {
    // Transmission mode; record error packet for later retransmission.
    assert (my_packetID <= 32767);
    assert (m_numErrorPackets < max_errorPackets);
    m_errorPackets[m_numErrorPackets] = static_cast<short> (my_packetID);
    m_numErrorPackets++;
  }
  else {
    // Retransmission mode: Ignore every other packet (dummy packet).

    if ((my_packetID%2) == 1) {
#ifdef MCS_VERBOSE
      std::cout << "FileInput_MAC_LLC: Ignoring dummy packet." << std::endl;
#endif
      return;
    }

    // Set packet ID to equivalent FileInput block index.
    my_packetID = ((my_packetID-m_num_packets) - (1-(m_num_packets%2)))/2;
    assert (m_numErrorPackets < max_errorPackets);
    m_errorPackets[m_numErrorPackets] = m_errorPackets[my_packetID];
    m_numErrorPackets++;
  }

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Erroneous packets: ";
  for (int jj=0; jj<m_numErrorPackets; jj++) {
    if (jj) {
      std::cout << ", ";
    }
    std::cout << m_errorPackets[jj];
  }
  std::cout << std::endl;
#endif
}

void
FileInput_MAC_LLC::signalHighWatermark ()
{
  // Signal stop.
  m_bPauseFlow = true;
}

void
FileInput_MAC_LLC::signalLowWatermark ()
{
  // Signal resume.
  m_bPauseFlow = false;
}

void
FileInput_MAC_LLC::signalEmpty ()
{
  // Signal empty.
  m_bPauseFlow = false;
}

// Generic thread OS signature which calls component specific thread function.
void
FileInput_MAC_LLC::RunWorker (void * param)
{
  FileInput_MAC_LLC * component =
    (FileInput_MAC_LLC *) param;

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Component is running." << std::endl;
#endif

  component->RunLoop ();

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Component is stopping." << std::endl;
#endif
}

// Inner loop function that checks for a stop command.
status
FileInput_MAC_LLC::RunLoop ()
{
  m_bStop = false;
  m_eof = false;

  if (!Init ()) {
    return fail;
  }

  // Transmit entire file initially
  SCE::OS::sleep(1000);

  while (!m_bStop && !m_eof) {
    if (m_bPauseFlow) {
      SCE::OS::sleep (10);
      continue;
    }

    Run ();
  }

  /*
   * Initial transmit complete. Send dummy packet(s) if necessary to
   * push data through components.
   */

  if (!(m_packetCounter % 2)) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Sending dummy packet." << std::endl;
#endif
    AssembleDummyPacket ();
    m_fileout->pushData (m_output, m_num_data_elems);
  }

  // Retransmit erroneous packets.
  unsigned int errorPacketIndex = 0;
  unsigned int packetID = 0;

  while (!m_bStop) {
    // If not at end of m_errorPackets buffer, retransmit erroneous packets.

    if ((m_numErrorPackets-errorPacketIndex)>0) {
      // Set packetID to first item in array.
      packetID = m_errorPackets[errorPacketIndex];
      errorPacketIndex++;

      // Assemble packet for retransmission.
      int num_samples;
      if (AssemblePacket (packetID, num_samples) != success) {
	return fail;
      }

#ifdef MCS_VERBOSE
      std::cout << "FileInput_MAC_LLC: Retransmitting packet "
		<< packetID
		<< std::endl;
#endif

      memset (m_output+num_samples, 0x0001, m_num_data_elems-num_samples);
      m_fileout->pushData (m_output, m_num_data_elems);

      /*
       * Push dummy packet after each retransmission; this is necessary
       * for data flow and allows ACK/NAK to be received.
       */

      AssembleDummyPacket ();
      m_fileout->pushData (m_output, m_num_data_elems);
    }

    SCE::OS::sleep (1000);
  }

  // Close input file if open.

  if (m_file_in) {
    fclose (m_file_in);
  }

  m_file_in = 0;
  m_threadrunning = false;
  m_thread.detach ();
  return success;
}

void
FileInput_MAC_LLC::start ()
  throw (CORBA::SystemException, CF::Resource::StartError)
{
  // Invoke our output interface controls.
  if (m_llc_ctrl_out->IsConnected()) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Starting remote LLC control stream."
	      << std::endl;
#endif
    m_llc_ctrl_out->getRemotePort()->startStream();
  }

  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start();
}

void
FileInput_MAC_LLC::stop ()
  throw (CORBA::SystemException, CF::Resource::StopError)
{
  m_bStop = true;

  // Invoke our output interface controls.
  if (m_llc_ctrl_out->IsConnected()) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Stopping remote LLC control stream."
	      << std::endl;
#endif
    m_llc_ctrl_out->getRemotePort()->stopStream();
  }
}

CORBA::Object_ptr
FileInput_MAC_LLC::getPort (const CORBA::Char * name)
  throw (CORBA::SystemException, CF::PortSupplier::UnknownPort)
{
  std::string temp (name);

  if (temp == "FileInput_MAC_LLC_Out") {
    return m_fileout->getPort ();
  }
  else if (temp == "MAC_ErrCtrl_Input") {
    return m_mac_err_in->getPort ();
  }
  else if (temp == "MAC_FlowCtrl_Input") {
    return m_mac_flow_ctrl_in->getPort ();
  }
  else if (temp == "FileInput_MAC_LLC_LLC_Ctrl_Out") {
    return m_llc_ctrl_out->getPort ();

  }
  else {
    throw CF::PortSupplier::UnknownPort ();
  }
}

// Inner loop method that performs component business logic.
status
FileInput_MAC_LLC::Run ()
{
  assert (m_file_in);

  if (!m_header_sent) {
#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Sending header."
	      << std::endl;
#endif
    m_fileout->pushData (m_output, 72);
    m_header_sent = true;
  }

  // Read 1 packet of length m_num_data_elems
  m_output_count = fread (m_output, m_data_size, m_num_data_elems, m_file_in);

  m_packetCounter++;
  if (m_output_count > 0) {
    if (m_output_count != m_num_data_elems) {
      // This is the last packet.
      if (!feof (m_file_in)) {
	// Error reading file.
	m_bStop = true;
	return fail;
      }

      memset (m_output+m_output_count, 0x01, m_num_data_elems-m_num_bytes_last_packet);
    }

#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: Sending packet "
	      << m_packetCounter
	      << "; " << m_output_count << " octets."
	      << std::endl;
#endif

    m_fileout->pushData (m_output, m_num_data_elems);
  }

  if (feof (m_file_in)){
    if (m_output_count == 0) {
      m_packetCounter--;
    }

#ifdef MCS_VERBOSE
    std::cout << "FileInput_MAC_LLC: EOF after "
	      << m_packetCounter << " packets."
	      << std::endl;
    m_eof = true;
#endif
  }

  SCE::OS::sleep (1000);
  return success;
}
